#!/usr/bin/perl
# cybozu2ical: Convert Cybozu Office 6 calendar into iCal format
#
# $Id: cybozu2ical 350 2006-11-19 11:17:08Z ogawa $

use strict;
use lib 'lib';

use YAML;
use Encode qw/decode_utf8 encode/;
use WWW::CybozuOffice6::Calendar;
use Data::ICal;
use Data::ICal::Entry::Event;
use Data::ICal::Entry::TimeZone;
use Data::ICal::Entry::TimeZone::Standard;
use DateTime;
use Pod::Usage;
use Getopt::Long;

our $VERSION = '0.11';

my %opt = (conf => 'config.yaml');
GetOptions(\%opt, 'help', 'conf=s') or pod2usage(2);
pod2usage(1) if $opt{help};

my $cfg = YAML::LoadFile($opt{conf});

my $time_zone = $cfg->{time_zone} || 'Asia/Tokyo';

my $vcalendar = Data::ICal->new();
$vcalendar->add_properties(
    prodid   => "-//as-is.net/Cybozu2ICal $VERSION//EN",
    calscale => 'GREGORIAN',
    method   => 'PUBLISH',
    $cfg->{calname} ? ('X-WR-CALNAME' => $cfg->{calname}) : (),
    'X-WR-TIMEZONE' => $time_zone
);

my $cal = WWW::CybozuOffice6::Calendar->new(%$cfg);
for my $item ($cal->get_items()) {
    my $vevent = Data::ICal::Entry::Event->new();
    my %args = (
	summary     => decode_utf8($item->{summary}),
	description => decode_utf8($item->{description}),
	created     => to_icaldate($item->{created}),
	dtstamp     => to_icaldate($item->{modified})
    );

    if ($item->{is_full_day}) {
	$args{dtstart} = [to_icaldate($item->{start}, 1), { VALUE => 'DATE' }];
	$args{dtend}   = [to_icaldate($item->{end},   1), { VALUE => 'DATE' }];
    } else {
	$args{dtstart} = [to_icaldate($item->{start}, 0), { TZID => $time_zone }];
	$args{dtend}   = [to_icaldate($item->{end},   0), { TZID => $time_zone }];
    }

    # handle frequency
    if ($item->{frequency}) {
	my $freq = $item->{frequency};
	my %rrules = $freq ne 'WEEKDAYS' ?
	    ( FREQ => $freq ) :
	    ( FREQ => 'WEEKLY', WKST => 'SU', BYDAY => 'MO,TU,WE,TH,FR' );
	$rrules{UNTIL} = to_icaldate($item->{until}, 1) if $item->{until};
	$args{rrule} = join ';', map { $_ . '=' . $rrules{$_} } keys %rrules;
    }

    $vevent->add_properties(%args);
    $vcalendar->add_entry($vevent);
}

my $vtimezone = Data::ICal::Entry::TimeZone->new();
$vtimezone->add_properties(tzid => $time_zone);

# probably we need to support the Daylight Saving Time
my $standard = Data::ICal::Entry::TimeZone::Standard->new();

my $std = DateTime->new(year => 1970, month => 1, day => 1,
			hour => 0, minute => 0, second => 0,
			time_zone => $time_zone);
my $offset = DateTime::TimeZone::offset_as_string($std->offset) || '+0900';
my $tzname = $cfg->{tzname} || 'JST';

$standard->add_properties(
    tzoffsetfrom => $offset,
    tzoffsetto   => $offset,
    tzname       => $tzname,
    dtstart      => to_icaldate($std)
);
$vtimezone->add_entry($standard);

$vcalendar->add_entry($vtimezone);

print encode_($cfg->{output_encoding} || 'utf8', $vcalendar->as_string);

sub to_icaldate {
    my($dt, $is_full_day) = @_;
    $is_full_day ?
	$dt->ymd('') :
	$dt->ymd('') . 'T' . $dt->hms('') . ($dt->time_zone->is_utc ? 'Z' : '');
}

sub encode_ {
    my($enc, $text) = @_;
    if ($enc eq 'ncr') {
	$text =~ s/(\P{ASCII})/sprintf("&#%d;", ord($1))/eg;
    } else {
	$text = encode($enc, $text);
    }
    $text;
}

1;
__END__

=head1 NAME

cybozu2ical - Convert Cybozu Office 6 calendar into iCal format

=head1 SYNOPSIS

  % cybozu2ical
  % cybozu2ical --conf /path/to/config.yaml

=head1 DESCRIPTION

C<cybozu2ical> is a command line application that fetches Cybozu
Office 6 calendar items and converts them into a iCal file.  It allows
you to easily integrate the Cybozu Calendar into iCalendar-enabled
Calendar applications, such as Microsoft Outlook, Apple iCal, and of
course, Google Calendar.

You can run this via crontab, for example, every 1 hour.

=head1 REQUIREMENT

This application requires perl 5.8.0 with following Perl modules
installed on your box.

=over 4

=item WWW::CybozuOffice6::Calendar

=item Text::CSV_XS

=item Data::ICal

=item DateTime

=item YAML

=back

=head1 OPTIONS

This application has a command-line option as follows:

=over 4

=item --conf /path/to/config.yaml

Specifies the path to a configuration file. By default, C<config.yaml>
in the current directory.

=back

=head1 CONFIGURATION

The distributions includes a sample configuration file
C<config.yaml.sample>. You can rename it to C<config.yaml> and
configure C<cybozu2ical>.

=over 4

=item cybozu_url

Set the URL of your Cybozu Office 6.

=item username, userid

Set your username or userid for Cybozu Office 6.

=item password

Set your password for Cybozu Office 6.

=item time_zone

Set the timezone of your Cybozu Office 6 (e.g., Asia/Tokyo).

=item tzname

Set the short timezone name of your Cybozu Office 6 (e.g., JST).

=back

=head1 DEVELOPMENT

The development version is always available from the following
subversion repository:

  http://code.as-is.net/svn/public/cybozu2ical/trunk/

You can browse the files via SVN::Web from the following:

  http://code.as-is.net/viewvc/public/cybozu2ical/trunk/

Any comments, suggestions, or patches are welcome.

=head1 AUTHOR

Hirotaka Ogawa E<lt>hirotaka.ogawa at gmail.comE<gt>

This script is free software and licensed under the same terms as Perl
(Artistic/GPL).

=cut
